package com.geline.mybatisplus.context;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.ParameterizedTypeImpl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.geline.cloud.core.BaseSessionService;
import com.geline.mybatisplus.annotation.One2Many;
import com.geline.mybatisplus.handler.MetaEntityHandler;
import com.geline.mybatisplus.handler.impl.DefaultMetaEntityHandler;
import com.geline.mybatisplus.mapper.CurdMapper;
import com.geline.mybatisplus.mapper.JdbcTemplateCurdMapper;
import com.geline.mybatisplus.mapper.MybatisPlusCurdMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

/**
 * 操作主子表添加、更新
 *
 * @author mengxin
 * @date 2025/4/12
 */
@Deprecated
@Slf4j
public class One2ManyFactory {

    private CurdMapper curdMapper;

    public One2ManyFactory(CurdMapper curdMapper){
        this.curdMapper = curdMapper;
    }

    public static One2ManyFactory useJdbcTemplate(BaseSessionService sessionService){
        MetaEntityHandler metaEntityHandler = new DefaultMetaEntityHandler(sessionService);
        CurdMapper curdMapper = new JdbcTemplateCurdMapper(SpringUtil.getBean(JdbcTemplate.class), metaEntityHandler);
        return new One2ManyFactory(curdMapper);
    }

    public static One2ManyFactory useMybatisPlus(){
        CurdMapper curdMapper = new MybatisPlusCurdMapper();
        return new One2ManyFactory(curdMapper);
    }

    public static One2ManyFactory useCurdMapper(CurdMapper curdMapper){
        return new One2ManyFactory(curdMapper);
    }

    public <T> boolean save(Class<?> entityClass, Object object) {
        T bean = (T) BeanUtil.toBean(object, entityClass);
        //添加主表记录
        this.curdMapper.save(bean);
        //回填主表记录的id值
        BeanUtil.copyProperties(bean, object);
        Serializable mainIdValue = (Serializable) BeanMap.create(bean).get("id");
        //添加关联表记录
        saveOne2Many(object, mainIdValue, false);
        return true;
    }

    public <T> boolean updateById(Class<T> entityClass, Object object) {
        T bean = BeanUtil.toBean(object, entityClass);
        //更新主表记录
        this.curdMapper.updateById(bean);
        Serializable mainIdValue = (Serializable) BeanMap.create(bean).get("id");
        //先删除旧的关联记录，再添加新的关联表记录
        saveOne2Many(object, mainIdValue, true);
        return true;
    }

    public <T> boolean saveBatch(Collection<T> entityList){
        return this.curdMapper.saveBatch(entityList);
    }

    public <T> boolean removeById(Class<T> entityClass, Serializable id) {
        return curdMapper.removeById(id, entityClass);
    }

    public <T> boolean removeByIds(Class<T> entityClass, Collection<?> ids) {
        return curdMapper.removeByIds(ids, entityClass);
    }

    public <T> T getById(Class<T> entityClass, Serializable id) {
        return curdMapper.getById(id, entityClass);
    }

    public <T, V> V getById(Class<T> entityClass, Serializable id, Class<V> voClass) {
        T object = curdMapper.getById(id, entityClass);
        V bean = BeanUtil.toBean(object, voClass);

        JdbcTemplate jdbcTemplate = SpringUtil.getBean(JdbcTemplate.class);
        for(Field field : voClass.getDeclaredFields()) {
            One2Many annotation = field.getDeclaredAnnotation(One2Many.class);
            if(annotation != null) {
                Type type = field.getGenericType();
                if (type instanceof ParameterizedType) {
                    ParameterizedTypeImpl parameterizedType = (ParameterizedTypeImpl) type;
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    Class<?> rowClass = (Class<?>) actualTypeArguments[0];
                    String value = id instanceof CharSequence ? StrUtil.wrap(id.toString(), "'") : id.toString();
                    String sql = String.format("select * from %s where %s=%s", annotation.table(), annotation.foreignKey(), value);
                    List<?> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(rowClass));
                    BeanMap.create(bean).put(field.getName(), query);
                }
            }
        }
        return bean;
    }

    //添加关联表记录
    protected void saveOne2Many(Object object, Serializable mainIdValue, boolean deleteOld){
        for(Field field : object.getClass().getDeclaredFields()) {
            One2Many annotation = field.getAnnotation(One2Many.class);
            if(annotation != null) {
                TableInfo tableInfo = TableInfoHelper.getTableInfo(annotation.table());
                if(deleteOld){
                    //删除关联表数据
                    curdMapper.removeById(mainIdValue, annotation.entityClass());
                }

                try {
                    field.setAccessible(true);
                    Collection<Object> list = (Collection<Object>) field.get(object);
                    if(list != null) {
                        MetaObjectHandler metaObjectHandler = SpringUtil.getBean(MetaObjectHandler.class);
                        for(Object item : list) {
                            Object bean = BeanUtil.toBean(item, tableInfo.getEntityType());
                            String mainIdKey = StrUtil.toCamelCase(annotation.foreignKey());
                            BeanMap.create(bean).put(mainIdKey, mainIdValue);
                            MetaObject metaObject = SystemMetaObject.forObject(bean);
                            metaObjectHandler.insertFill(metaObject);
                            BeanMap beanMap = BeanMap.create(bean);
                            beanMap.put(mainIdKey, mainIdValue);
                            curdMapper.save(bean);
                        }
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
